#include "OpenGL.h"

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <sstream>
#include <windows.h>
#include <olectl.h>
#include "GObjects.h"

#define PiBy180 0.01745329251994329576923690768489F
using namespace std;

/*
	made the texture vector object static...
	thus avoiding texture redundency in the GPU memory...
*/
vector<Texture> GObjects::tVector;

//static variables definition
vector<void *> GObjects::allTheShips;
vector<void *> GObjects::allTheIslands;
vector<void *> GObjects::allTheCollectibles;
vector<Terrain *> GObjects::allTheTerrains;

bool GObjects::cannonBallImmediateExplosion = false;
bool GObjects::physicsDebugInfo = false;
GObjects::GObjects(){
	//x = y = z = 1.0f;
	yRot = 0.0f;

	geometryModified = true;
	displayListId = glGenLists(1);
	nVertices = nFaces = nVectorNormals = nVectorTextures = 0;
	nPoints = 0;
	nMaterials = 0;
	modelsFrontFaceOrientation = GL_CCW;
}
void GObjects::setObjectPositionAndOrientation(float x1,float y1,float z1,float angle){
	position.x = x1;
	position.y = y1;
	position.z = z1;
	yRot = angle;	
	angle = angle * PiBy180 * 0.5f; // angle= (alpha/2);
	//orient y-axis pointing upwards
	orientation.r = cos(angle);
	orientation.i = 0.0f;
	orientation.j = sin(angle);
	orientation.k = 0.0f;
}
void GObjects::addToAllTheShips(void *thisObject){
	allTheShips.push_back(thisObject);
}
void GObjects::addToAllTheIslands(void *thisObject){
	allTheIslands.push_back(thisObject);
}
void GObjects::addToAllTheCollectibles(void *thisObject){
	allTheCollectibles.push_back(thisObject);
}
int GObjects::loadGeometryObj(string fileName,int nF, int nV, int nVN, int nVT){
	ifstream inFile(gInputFolder + fileName);
	if(!inFile.is_open()) {
		return -1;
	}
	//init memory if  you already know the size of v,f,vt and vn
	fVector.reserve(nF);
	vVector.reserve(nV);
	vtVector.reserve(nVT);
	vnVector.reserve(nVN);

	string text;
	Vertex tVertex;
	Faces tFace;
	int lastFaceType = -1;
	int color=0;;
	char cValue;

	int currentShininess=1;	string currentGroupName="", currentMaterialName="";

	while(!inFile.eof()){
		inFile >> text;
		switch(text[0]){
		case '#': //ignore comment lines
			inFile.ignore(256,'\n');
			break;
		case 'm':
			if(text == "mtllib"){
				inFile >> materialFile;
				loadMaterial(materialFile);
			}
			break;
		case 'v':
			inFile >> tVertex.x >> tVertex.y >> tVertex.z;
			if(inFile.fail()){ //bug "v0.9234 versioning in file", fixed... here only v will be read as a string, if it is not followed by 3 floats then ignore it... it's not a vertex daa...
				inFile.clear();
			}else{
				if(text == "v"){
					vVector.push_back(tVertex);
					nVertices++;
				}else if(text == "vn"){
					vnVector.push_back(tVertex);
					nVectorNormals++;
				}else if(text == "vt"){
					vtVector.push_back(tVertex);
 					nVectorTextures++;
				}
			}
			break;
		case 's':
			if(text == "s"){
				inFile >> currentShininess;
				if(inFile.fail()) { //if "s off", reading off into (int) currentShininess will create error, clear and continue...
					inFile.clear();
					inFile >> text;
					if(text == "off"){ //confirm if the error is caused by "s off", if "yes", set shininess to 0 and continue...
						currentShininess = 0;
					}else if(inFile.fail()){ // if "no", again clear the stream and continue... 
						inFile.clear();
					}
				}
			}//if(text == "s")
			break;
		case 'g':
			if(text == "g"){
				inFile >> currentGroupName;
				if(inFile.fail()){ //there can be a text like g0.923 in a file, similar to v0.9234... taking precautions in advance...
					inFile.clear();
				}
			}
			break;
		case 'u':
			if(text == "usemtl"){
				inFile >> currentMaterialName;	
			}
			break;
		case 'f':
			if(text == "f"){
				VTN tVTN;
				do{
					string sString;
					stringstream sStream;
					inFile >> sString;
					sStream << sString;
					bool found = false;
					if(!found){
						sStream >> tVTN.vI >> cValue >> tVTN.vtI >> cValue >> tVTN.vnI;
						if(!sStream.fail()){
							lastFaceType = 3;
							found = true;
						} else 
							sStream.clear();
					}
					if(!found){
						sStream >> tVTN.vI >> cValue >> cValue >> tVTN.vnI;
						if(!sStream.fail()){
							tVTN.vtI = 0;
							lastFaceType = 4;
							found = true;
						} else 
							sStream.clear();
					} 
					if(!found){
						sStream >> tVTN.vI >> cValue >> tVTN.vnI;
						if(!sStream.fail()){
							tVTN.vtI = 0;
							lastFaceType = 2;
							found = true;
						} else 
							sStream.clear();
					} 
					if(!found){
						sStream >> tVTN.vI;
						if(!sStream.fail()){
							tVTN.vtI = 0;
							tVTN.vnI = 0;
							lastFaceType = 1;
							found = true;
						} else 
							sStream.clear();
					} 

					if(found == true){
						tFace.faceType = lastFaceType;
						tFace.vtn.push_back(tVTN);
					} else {//unget the last string read... so as to set the getPointer properly...
						int n = sString.length();
						for(int i=0;i<n;i++){
							inFile.unget();
						}
						inFile.clear();
						break;//from infinite loop
					}

				}while(1);
				if(tFace.vtn.size() > 0){
					//tFace.shininess = currentShininess;
					tFace.faceGroupName = currentGroupName;
					tFace.faceMaterialName = currentMaterialName;
					fVector.push_back(tFace);
					nFaces++;
					
				}
				tFace.vtn.clear();
			}
		break;
		//default:cout << text << " ";
		}//switch(text[0])
	}

	inFile.close();
	return 0;
}
int GObjects::drawGeometry(cyclone::RigidBody *thisObjectBody){
	//z += 0.1f;
	Vertex v,vt,vn;
	VTN tVTN;
	
	float mat[16];
	thisObjectBody->getGLTransform(mat);
	if(geometryModified) {
		if(displayListId < 1){//non zero
			cout << name << ": Invalid displayListId" << endl;
			int x;cin >> x;
		}
		glNewList(displayListId, GL_COMPILE);

		glMatrixMode(GL_MODELVIEW_MATRIX);
		glPushMatrix();
			glFrontFace(modelsFrontFaceOrientation);//GL_CCW/GL_CW
			//glColor3f(1.0f,1.0f,1.0f);
			int f=0;
			string currentMaterial = "";
			int currentTexture=0; 
			for(f=0;f<nFaces; f++){
				//find the position of the material in mVector
				if(currentMaterial != fVector[f].faceMaterialName){
					currentMaterial = fVector[f].faceMaterialName;
					int mIndex=0,mCount= mVector.size();
					for(int m=0; m<mCount; m++){
						if(mVector[m].materialName == fVector[f].faceMaterialName){
							mIndex = m;
							break;
						}
					}
					//set material properties...
					glMaterialfv(GL_FRONT, GL_AMBIENT, mVector[mIndex].Ka);//array
					glMaterialfv(GL_FRONT, GL_DIFFUSE, mVector[mIndex].Kd);//array
					glMaterialfv(GL_FRONT, GL_SPECULAR, mVector[mIndex].Ks);//array
					glMaterialf(GL_FRONT, GL_SHININESS, mVector[mIndex].Ns);//int
					glMaterialfv(GL_FRONT, GL_EMISSION, mVector[mIndex].Ke);//array
					
					if(mVector[mIndex].map_Ka == ""){
						glBindTexture(GL_TEXTURE_2D,NULL);
					}else{
						int tn = tVector.size();
						int tIndex=0;
						for(int t=0; t<tn; t++){
							if(tVector[t].textureName == (gInputFolder + mVector[mIndex].map_Ka)){
								tIndex = t;
								break;
							}
						}
						glActiveTexture(GL_TEXTURE0);
						glBindTexture(GL_TEXTURE_2D, tVector[tIndex].textureId);
					}
				}//if

				bool enableTextures = false;
				bool enableNormals = false;
				switch(fVector[f].faceType){
				//case 1:	break; //option 1 already set... false,false
				case 3:// enable both texture and normals
					enableTextures = true;
				case 2://enable only normals
				case 4:
					enableNormals = true;
				case 1: // do nothing
					break;
				}

				if(enableTextures == false){
					glDisable(GL_TEXTURE_2D);
				} else {
					glEnable(GL_TEXTURE_2D);
				}

				//draw polygon...
				glBegin(GL_POLYGON);
					int n = fVector[f].vtn.size();
					for(int i=0;i<n;i++){
						//vertex texture co-ordinates
						tVTN = fVector[f].vtn[i];
						if(enableTextures == true){
							vt = vtVector[tVTN.vtI - 1];
							glTexCoord3f(vt.x,vt.y,vt.z);
						}
						//vertex normal co-ordinates
						if(enableNormals == true){
							vn = vnVector[tVTN.vnI - 1];
							glNormal3f(vn.x,vn.y,vn.z);
						}
						//vector co-ordinates
						v = vVector[tVTN.vI - 1];
						glVertex3f(v.x,v.y,v.z);
					}
				glEnd();
			}
		glPopMatrix();
		//glDisable(GL_TEXTURE_2D);
		glEndList();
		geometryModified = false;
	}
	else
	{
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
			glMultMatrixf(mat);
			glRotated(yRot,0,1,0);
			glScaled(scale,scale,scale);	
			glCallList(displayListId);
		glPopMatrix();
	}
	return 0;
}

int GObjects::loadMaterial(string mFileName){
	ifstream inFile(gInputFolder + mFileName);
	if(!inFile.is_open()) {
		return -1;
	}
	bool materialFound=false;
	string text = "";
	nMaterials = 0;
	while(!inFile.eof()){
		if(text != "newmtl") 
			inFile >> text;
		if(text == "newmtl"){
			Material tMaterial;//create a new tMaterial object every time you read a new material
			inFile >> tMaterial.materialName;
			materialFound = true;
			do {
				inFile >> text;
				if(text == "Kd" ){
					inFile >> tMaterial.Kd[0] >> tMaterial.Kd[1] >> tMaterial.Kd[2]; 
				} else if(text == "Ks"){
					inFile >> tMaterial.Ks[0] >> tMaterial.Ks[1] >> tMaterial.Ks[2]; 
				}else if(text == "Ka"){
					inFile >> tMaterial.Ka[0] >> tMaterial.Ka[1] >> tMaterial.Ka[2]; 
				}else if(text == "Ke") {
					inFile >> tMaterial.Ke[0] >> tMaterial.Ke[1] >> tMaterial.Ke[2]; 
				}else if(text == "Tf"){
					inFile >> tMaterial.Tf[0] >> tMaterial.Tf[1] >> tMaterial.Tf[2]; 
				}else if(text == "d") {
					inFile >> tMaterial.d;
					tMaterial.Kd[3] = tMaterial.Ks[3] = tMaterial.Ka[3] = tMaterial.Ke[3] = tMaterial.d;
				}else if(text == "Tr") {
					inFile >> tMaterial.Tr;
				}else if(text == "Ns") {
					inFile >> tMaterial.Ns;
				}else if(text == "Ni") {
					inFile >> tMaterial.Ni;
				}else if(text == "illum") {
					inFile >> tMaterial.illum;
				}else if(text == "map_Ka"){
					inFile >> tMaterial.map_Ka;
					addTexturesToVector(gInputFolder + tMaterial.map_Ka);
				}else if(text == "map_Kd"){
					inFile >> tMaterial.map_Kd;
				}else if(text == "map_d"){
					inFile >> tMaterial.map_d;
				}else if(text == "newmtl" || materialFound == true ){
					mVector.push_back(tMaterial);
					nMaterials++;
					break;//re-start from while(!inFile.eof()) to read a new material
				}

				if(inFile.fail()) {
					inFile.clear();
					break;
				}
			}while(1); //don't know how many entries are there for materials...
		}//if(text == "newmtl")
	}
	inFile.close();
	return 0;
}
int GObjects::addTexturesToVector(string tFileName){
	bool found=false;
	int nTextures = tVector.size();
	for(int i=0;i<nTextures; i++){
		if(tVector[i].textureName == tFileName){
			found = true;
		}
	}
	if(!found){
		Texture tTexture;
		tTexture.textureId = tTexture.LoadTexture(tFileName);
		tTexture.textureName = tFileName;
		//cout << "	Texture(Added): " << tFileName << endl;
		tVector.push_back(tTexture);
	}
	return 0;
}
 